package cn.sc.summer.mybatis.config.datasource;

import cn.hutool.extra.spring.SpringUtil;
import cn.sc.summer.mybatis.config.SqlInjectorConfig;
import cn.sc.summer.mybatis.config.TenantConfig;
import cn.sc.summer.mybatis.config.interceptor.DataSourceInterceptor;
import cn.sc.summer.mybatis.config.interceptor.DecryptInterceptor;
import cn.sc.summer.mybatis.config.interceptor.MybatisLogSqlInterceptor;
import cn.sc.summer.mybatis.context.DynamicDataSourceContext;
import cn.sc.summer.mybatis.properties.TenantProperties;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import com.baomidou.mybatisplus.autoconfigure.SpringBootVFS;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import cn.sc.summer.constant.mybatisplus.MybatisConstant;
import cn.sc.summer.constant.mybatisplus.datasource.DataSourceConstant;
import cn.sc.summer.mybatis.handler.MyMetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;

/**
 * 类名：多数据源配置信息
 *
 * @author a-xin
 * @date 2024/2/1 14:52
 */
@Slf4j
@Configuration
public class DataSourceConfig {

    @Resource
    private MybatisPlusProperties properties;

    @Resource
    private TenantProperties tenantProperties;

    /**
     * MyBatis 字段值自动填充
     */
    @Bean
    @Primary
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        log.info("==> Start initialization the sqlSessionFactory...");
        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        DynamicDataSourceContext dynamicDataSourceContext = SpringUtil.getBean(DataSourceConstant.DYNAMIC_DATA_SOURCE, DynamicDataSourceContext.class);
        //加载数据源
        factory.setDataSource(dynamicDataSourceContext);
        //全局配置
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setBanner(Boolean.FALSE);
        //配置填充器
        globalConfig.setMetaObjectHandler(new MyMetaObjectHandler());
        globalConfig.setSqlInjector(new SqlInjectorConfig());
        factory.setGlobalConfig(globalConfig);

        // mybatis本身的核心库在springboot打包成jar后有个bug，无法完成别名的扫描
        factory.setVfs(SpringBootVFS.class);

        MybatisConfiguration configuration = this.properties.getConfiguration();
        if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
            configuration = new MybatisConfiguration();
        }

        factory.setConfiguration(configuration);
        if (this.properties.getConfigurationProperties() != null) {
            factory.setConfigurationProperties(this.properties.getConfigurationProperties());
        }

        if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
            factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
        }

        if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
            factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
        }

        if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
            factory.setMapperLocations(this.properties.resolveMapperLocations());
        }

        factory.setPlugins(mybatisPlusInterceptor());
        return factory.getObject();
    }

    /**
     * 自定义过滤器配置
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        log.info("==> Start add tenant filter...");
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        Boolean enabled = tenantProperties.getEnabled();
        log.info("==> The tenant filter status: {}", enabled);
        if (enabled) {
            interceptor.addInnerInterceptor(new TenantConfig(tenantProperties.getIgnore(), MybatisConstant.TENANT_FILED_TABLE));
            log.info("==> Tenants ignore tables: [ {} ]", String.join(", ", tenantProperties.getIgnore()));
        }

        log.info("==> Start adding a pagination blocker...");
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        // 添加乐观锁拦截器
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        // 读写分离拦截器，数据加密
        interceptor.addInnerInterceptor(new DataSourceInterceptor());
        return interceptor;
    }

    /**
     * 添加自定义拦截器
     */
    @Bean
    public Boolean mySqlLogger(SqlSessionFactory sqlSessionFactory) {
        // 自定义sql日志打印
        sqlSessionFactory.getConfiguration()
                .addInterceptor(new MybatisLogSqlInterceptor());
        // 数据解密
        sqlSessionFactory.getConfiguration()
                .addInterceptor(new DecryptInterceptor());
        return Boolean.TRUE;
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}


